// export function openDB(dbName, version = 1) {
//   if (dbName == null) {
//     throw new Error('dbName is required');
//   }
//   return new Promise((resolve, reject) => {
//     const DB = window.indexedDB;
//     // 数据库对象
//     let db;
//     // 打开数据库，若没有则会创建
//     const request = DB.open(dbName, version);
//     // 数据库打开成功回调
//     request.onsuccess = e => {
//       db = e.target.result;
//       resolve(db);
//     };
//     request.onerror = error => {
//       reject(error);
//     };
//     /**
//      * open() 打开之前不存在的数据库会被执行
//      * 升级数据库版本会被执行
//      */
//     request.onupgradeneeded = e => {
//       db = e.target.result;
//       // 创建一个用户库（用户表）
//       createObjectStore(db, 'user', {
//         // 主键
//         keyPath: 'id',
//       })
//         .then(store => {
//           // 创建索引 在后面查询数据的时候可以根据索引查
//           store.createIndex('id', 'id', {
//             unique: true, // 是否唯一
//           });
//           store.createIndex('name', 'name', { unique: false });
//           store.createIndex('age', 'age', { unique: false });
//         })
//         .catch(() => {
//           console.log('已创建，略过');
//         });
//     };
//   });
// }
//
// function createObjectStore(db, name, options = {}) {
//   return new Promise((resolve, reject) => {
//     if (!db.objectStoreNames.contains(name)) {
//       const objectStore = db.createObjectStore(name, options);
//       resolve(objectStore);
//     }
//     reject();
//   });
// }

export class DB {
  constructor(dbName, version = 1) {
    // 数据库名
    this.dbName = dbName;
    // 数据库版本号
    this.version = version;
    // 数据库对象
    this.db = null;
  }

  open() {
    return new Promise((resolve, reject) => {
      // 打开数据库，若没有则会创建
      const request = window.indexedDB.open(this.dbName, this.version);
      // 数据库打开成功回调
      request.onsuccess = e => {
        this.db = e.target.result;
        resolve(this.db);
      };
      request.onerror = error => {
        reject(error);
      };

      /**
       * open() 打开之前不存在的数据库会被执行
       * 升级数据库版本会被执行
       */
      request.onupgradeneeded = async e => {
        this.db = e.target.result;

        try {
          // 创建一个用户库（用户表）
          const store = await this.createStore('user', {
            // 主键
            keyPath: 'id',
          });
          // 创建索引 在后面查询数据的时候可以根据索引查
          store.createIndex('id', 'id', {
            unique: true, // 是否唯一
          });
          store.createIndex('name', 'name', { unique: false });
          store.createIndex('age', 'age', { unique: false });
        } catch (error) {}
      };
    });
  }

  // 创建表
  createStore(name, options = {}) {
    return new Promise((resolve, reject) => {
      if (!this.db.objectStoreNames.contains(name)) {
        const objectStore = this.db.createObjectStore(name, options);
        resolve(objectStore);
      }
      reject();
    });
  }

  addData(storeName, data) {
    return new Promise((resolve, reject) => {
      const request = this.db
        .transaction([storeName], 'readwrite') // 事务对象 指定表的名称和操作模式
        .objectStore(storeName) // 仓库对象
        .add(data);
      request.onsuccess = resolve;
      request.onerror = reject;
    });
  }

  getAllData(storeName) {
    return new Promise((resolve, reject) => {
      const request = this.db.transaction([storeName]).objectStore(storeName).getAll();
      request.onsuccess = () => resolve(request.result);
      request.onerror = reject;
    });
  }

  // 通过主键查询只能查询出一条数据
  getDataById(storeName, key) {
    return new Promise((resolve, reject) => {
      const request = this.db
        .transaction([storeName], 'readwrite') // 事务对象 指定表的名称和操作模式
        .objectStore(storeName) // 仓库对象
        .get(key);
      request.onsuccess = () => resolve(request.result);
      request.onerror = reject;
    });
  }

  // 通过游标查询数据
  cursorGetData(storeName) {
    return new Promise((resolve, reject) => {
      const list = [];
      const store = this.db.transaction(storeName, 'readwrite').objectStore(storeName);
      const request = store.openCursor(); // 指针对象
      // onsuccess 会执行多次
      request.onsuccess = e => {
        const cursor = e.target.result;
        if (cursor) {
          list.push(cursor.value);
          // 逐行读取 直到全部读取完毕
          cursor.continue(); // 遍历了存储对象中的所有内容
          return;
        }
        resolve(list);
      };
      request.onerror = reject;
    });
  }

  /**
   * 通过索引查询数据
   * @param { string } storeName - 仓库名/表名
   * @param { string } indexName - 索引名称
   * @param { any } indexValue - 索引值
   */
  getDataByIndex(storeName, indexName, indexValue) {
    return new Promise((resolve, reject) => {
      const store = this.db.transaction(storeName, 'readwrite').objectStore(storeName);
      const request = store.index(indexName).get(indexValue);
      request.onerror = reject;
      request.onsuccess = e => resolve(e.target.result);
    });
  }

  /**
   * 通过索引和游标查询记录
   * 通过游标可以查询出全部的数据，通过索引查询的是单条数据
   * 将两者组合起来，可进行一个数据的筛选
   * @param { string } storeName - 仓库名/表名
   * @param { string } indexName - 索引名称
   * @param { any } indexValue - 索引值
   */
  cursorGetDataByIndex(storeName, indexName, indexValue) {
    return new Promise((resolve, reject) => {
      const list = [];
      const store = this.db.transaction(storeName, 'readwrite').objectStore(storeName);
      const request = store.index(indexName).openCursor(IDBKeyRange.only(indexValue));
      request.onsuccess = e => {
        const cursor = e.target.result;
        if (cursor) {
          list.push(cursor.value);
          // 逐行读取 直到全部读取完毕
          cursor.continue(); // 遍历了存储对象中的所有内容
          return;
        }
        resolve(list);
      };
      request.onerror = reject;
    });
  }

  /**
   * 实现分页查询
   * @param { string } storeName - 仓库名/表名
   * @param { string } indexName - 索引名称
   * @param { any } indexValue - 索引值
   * @param { number } pageCurrent - 分页页码
   * @param { number } pageSize - 分页查询条数
   */
  cursorGetDataByIndexAndPage(storeName, indexName, indexValue, pageCurrent, pageSize) {
    return new Promise((resolve, reject) => {
      const list = [];
      let counter = 0;
      // 是否跳过多少条查询
      let advanced = true;
      const store = this.db.transaction(storeName, 'readwrite').objectStore(storeName);
      const request = store.index(indexName).openCursor(IDBKeyRange.only(indexValue));
      request.onsuccess = e => {
        let cursor = e.target.result;
        if (pageCurrent > 1 && advanced) {
          advanced = false;
          cursor.advance((pageCurrent - 1) * pageSize); // 跳过多少条
          return;
        }
        if (cursor) {
          list.push(cursor.value);
          counter++;
          if (counter < pageSize) {
            // 遍历了存储对象中的所有内容
            cursor.continue();
          } else {
            cursor = null;
            resolve(list);
          }
        } else {
          resolve(list);
        }
      };
      request.onerror = reject;
    });
  }

  /**
   * 更新数据（如果数据库中没有该条数据，则新增，否则更新）
   * @param { string } storeName
   * @param { Record<string, any> } data
   */
  updateDB(storeName, data) {
    return new Promise((resolve, reject) => {
      const request = this.db.transaction(storeName, 'readwrite').objectStore(storeName).put(data);
      request.onsuccess = resolve;
      request.onerror = reject;
    });
  }

  /**
   * 通过主键删除数据（只能删除一条数据）
   * @param { string } storeName
   * @param { number } id
   */
  deleteDbById(storeName, id) {
    return new Promise((resolve, reject) => {
      const request = this.db.transaction(storeName, 'readwrite').objectStore(storeName).delete(id);
      request.onsuccess = resolve;
      request.onerror = reject;
    });
  }

  deleteDbByIds(storeName, ids) {
    return Promise.all(ids.map(id => this.deleteDbById(storeName, id)));
  }

  /**
   * // 通过索引和游标删除指定数据
   * @param { string } storeName
   * @param { string } indexName
   * @param { Object | Array } indexValue
   * @param { boolean = false } returnDeleteData
   * @returns {Promise<Record<string, any> | void>}
   */
  deleteDbByCursor(storeName, indexName, indexValue, returnDeleteData = false) {
    return new Promise((resolve, reject) => {
      const store = this.db.transaction(storeName, 'readwrite').objectStore(storeName);
      const request = store.index(indexName).openCursor(IDBKeyRange.only(indexValue));
      const queue = [];
      let index = -1;
      request.onsuccess = e => {
        const cursor = e.target.result;
        let deleteRequest;
        if (cursor) {
          const current = ++index;
          deleteRequest = cursor.delete(); // 请求删除当前项
          deleteRequest.onsuccess = () => {
            console.log('delete success');
            queue[current] = Promise.resolve({ indexName, indexValue, state: 'success' });
          };
          deleteRequest.onerror = () => {
            console.log('delete success');
            queue[current] = Promise.resolve({ indexName, indexValue, state: 'error' });
          };
          cursor.continue();
        } else {
          if (returnDeleteData) {
            return resolve(queue);
          }
          Promise.all(queue).then(res => {
            if (returnDeleteData) {
              return resolve(res);
            }
            resolve();
          });
        }
      };

      request.onerror = reject;
    });
  }

  // 关闭数据库
  closeDB() {
    this.db.close();
  }

  // 删除数据库
  deleteDB() {
    return new Promise((resolve, reject) => {
      const deleteRequest = window.indexedDB.deleteDatabase(this.dbName);
      deleteRequest.onsuccess = resolve;
      deleteRequest.onerror = reject;
    });
  }
}

// https://zhuanlan.zhihu.com/p/429086021
